﻿using System.Collections.Generic;
using SpiceNet.Diagnostics;
using SpiceNet.Circuits;
using SpiceNet.Simulations;
using SpiceNet.Parameters;

namespace SpiceNet.Models
{
    /// <summary>
    /// A model of a resistor
    /// </summary>
    public class ResistorModel : Model
    {
        /// <summary>
        /// Get a list of parameters
        /// </summary>
        public override Dictionary<string, IP> ParamTable => _pTable;

        #region Parameters
        private enum _c
        {
            RES_MOD_TC1 = 101, RES_MOD_TC2 = 102, RES_MOD_RSH = 103, RES_MOD_DEFWIDTH = 104, RES_MOD_NARROW = 105, RES_MOD_R = 106, RES_MOD_TNOM = 107
        }
        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "rsh", new IP(IF.IOPQ, (int)_c.RES_MOD_RSH, T.REAL, "Sheet resistance") },
            { "narrow", new IP(IF.IOPZ, (int)_c.RES_MOD_NARROW, T.REAL, "Narrowing of resistor") },
            { "tc1", new IP(IF.IOPQ, (int)_c.RES_MOD_TC1, T.REAL, "First order temp. coefficient") },
            { "tc2", new IP(IF.IOPQO, (int)_c.RES_MOD_TC2, T.REAL, "Second order temp. coefficient") },
            { "defw", new IP(IF.IOPX, (int)_c.RES_MOD_DEFWIDTH, T.REAL, "Default device width") },
            { "tnom", new IP(IF.IOPXU, (int)_c.RES_MOD_TNOM, T.REAL, "Parameter measurement temperature") },
            { "r", new IP(IF.IP, (int)_c.RES_MOD_R, T.FLAG, "Device is a resistor model") }
        };

        public Parameter<double> REStnom { get; } = new Parameter<double>();
        public Parameter<double> REStempCoeff1 { get; } = new Parameter<double>();
        public Parameter<double> REStempCoeff2 { get; } = new Parameter<double>();
        public Parameter<double> RESsheetRes { get; } = new Parameter<double>();
        public Parameter<double> RESdefWidth { get; } = new Parameter<double>(10.0e-6);
        public Parameter<double> RESnarrow { get; } = new Parameter<double>();
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        public ResistorModel(string name) : base(name) { }

        /// <summary>
        /// Set a parameter
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.RES_MOD_TNOM:
                    REStnom.Par((double)value + Circuit.CONSTCtoK);
                    break;
                case _c.RES_MOD_TC1:
                    REStempCoeff1.Par((double)value);
                    break;
                case _c.RES_MOD_TC2:
                    REStempCoeff2.Par((double)value);
                    break;
                case _c.RES_MOD_RSH:
                    RESsheetRes.Par((double)value);
                    break;
                case _c.RES_MOD_DEFWIDTH:
                    RESdefWidth.Par((double)value);
                    break;
                case _c.RES_MOD_NARROW:
                    RESnarrow.Par((double)value);
                    break;
                case _c.RES_MOD_R:
                    /* just being reassured by user that this is a resistor model */
                    /* no-op */
                    break;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Ask a parameter
        /// </summary>
        /// <param name="id"></param>
        /// <param name="ckt"></param>
        /// <returns></returns>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.RES_MOD_TNOM:
                    return REStnom - Circuit.CONSTCtoK;
                case _c.RES_MOD_TC1:
                    return REStempCoeff1.Value;
                case _c.RES_MOD_TC2:
                    return REStempCoeff2.Value;
                case _c.RES_MOD_RSH:
                    return RESsheetRes.Value;
                case _c.RES_MOD_DEFWIDTH:
                    return RESdefWidth.Value;
                case _c.RES_MOD_NARROW:
                    return RESnarrow.Value;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Load the noise analysis data
        /// </summary>
        /// <param name="ckt"></param>
        /// <param name="mode"></param>
        /// <param name="operation"></param>
        /// <param name="data"></param>
        /// <param name="outNdens"></param>
        public override void NoiseLoad(Circuit ckt, Noise.OutputMode mode, Noise.Operation operation, NoiseData data, ref double outNdens)
        {
            foreach (Instance inst in Instances)
                inst.NoiseLoad(ckt, mode, operation, data, ref outNdens, this);
        }
    }
}
